#include<filesystem>
#include<fstream>
#include <deque>
#include<string.h>
#include <iostream>
#include<stdlib.h>

#include "Resource.h"

const wchar_t source_folder_path[] = L".\\res";

const wchar_t target_header[] = L".\\include\\ResDef.h";

const wchar_t target_data[] = L".\\resbuilder\\resource.data";

using namespace std;
using namespace util::resource;

typedef struct{
    BinFile bin;
    const wchar_t * name;   
    const char * file_type;
}Record;

void ReadBin(filesystem::path filepath, BinFile& bin, const long f_size){
    std::ifstream in;
    in.open(filepath, std::ios_base::binary);
    if(!in.is_open()){
        return;
    }
    bin.data = new char[f_size + 1];
    bin.size = f_size;
    in.read(bin.data, f_size);
    in.close();
}

void GetBinFiles(const wchar_t * path, deque<Record>& bin_quene){
    int count = 0;
    for(auto files : filesystem::directory_iterator(path)){
        char * filepath;
        if(files.is_directory()){
            GetBinFiles(files.path().native().c_str(), bin_quene);
            continue;
        }
        if(files.is_regular_file()){
            BinFile bin;
            int iptr = 0;
            ReadBin(files.path(), bin, files.file_size());
            wchar_t * f_name = new wchar_t[64];
            const char * f_type;

            wcscpy(f_name, files.path().filename().native().c_str());  
            for(int i = 0; f_name[i] != 0; i++){
                if(f_name[i] == L'.')
                    iptr = i;
            }
            wcout << L"load: " << f_name << '\t';
            if(f_name[iptr] == L'.'){
                if(!wcscmp(f_name + iptr + 1, L"bmp")){
                    BmpFile bmp(bin);
                    wcout << L"bpp: " << bmp.info->biBitCount;  
                    f_type = "BMP";
                }
                else if(!wcscmp(f_name + iptr + 1, L"png")){
                    f_type = "PNG";
                }
                else if(!wcscmp(f_name + iptr + 1, L"mp4")){
                    f_type = "MP4";
                }
                else{
                    f_type = nullptr;
                }
                f_name[iptr] = 0;
            }
            wcout << endl; 
            bin_quene.push_back(Record{bin, f_name, f_type});    
        }
    }
}

void MergeBin(deque<Record>& files, const wchar_t * output_path){
    std::ofstream out(output_path, std::ios::binary);
    if(!out.is_open())
        throw "temp excption";
    for(auto file : files){
        const uintmax f_size = file.bin.size;
        out.write(reinterpret_cast<char*>(&file.bin.size), sizeof(uintmax));
        out.write(file.bin.data, f_size);
    }
    out.close();
}

void WriteResDefHeader(deque<Record> indexs, const wchar_t * path){
    ofstream out(path);
    if(!out.is_open())
        throw "failed to open file!";
    out << "#pragma once" << '\n' << '\n';
    out <<  "extern char _binary_resbuilder_resource_data_start;\n"
            "extern char _binary_resbuilder_resource_data_end;\n"
            "extern char _binary_resbuilder_resource_data_size;\n";
    char buffer[64] = {0};
    int count = 0;
    for(auto index : indexs){
        wcstombs(buffer, index.name, 64);
        out << "#define RES_DEfINE_";
        if(index.file_type != nullptr)
            out << index.file_type << '_' << buffer << ' ' << count++ << '\n';
        else
            out << "UNKNOWN_" << buffer << ' ' << count++ << '\n';
    }
    out.close();
}

int main(){
    cout << "start prepare program --" << endl << endl;
    deque<Record> list;
    GetBinFiles(source_folder_path, list);
    cout << "has found " << list.size() << " files." << endl << endl;
    MergeBin(list, target_data);
    WriteResDefHeader(list, target_header);
    for(auto file : list){
        delete[] file.bin.data;
        delete[] file.name;
    }
    cout << "completed" << endl;
    system("pause");
}